/**
 * Copyright 2014  XCL-Charts
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @Project XCL-Charts
 * @Description Android图表基类库
 * @author XiongChuanLiang<br                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               />(xcl_168@aliyun.com)
 * @license http://www.apache.org/licenses/  Apache v2 License
 * @version 2.1
 */
package com.droid.lib.controls.chart.renderer;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Path;
import android.graphics.PointF;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import com.droid.lib.controls.chart.common.DrawHelper;
import com.droid.lib.controls.chart.event.click.BarPosition;
import com.droid.lib.controls.chart.renderer.plot.PlotLegendRender;
import java.util.ArrayList;
import java.util.List;
import static com.droid.lib.controls.chart.renderer.XEnum.FunnelType.contrast;
import static com.droid.lib.controls.chart.renderer.XEnum.FunnelType.symmetry;

/**
 * @author XiongChuanLiang<br                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               />(xcl_168@aliyun.com)
 * @ClassName FunnelChart
 * @Description 漏斗图基类
 */

public class FunnelChart extends EventChart {

    private static final String TAG = "FunnelChart";

    private List<FunnelData> mDataSet = new ArrayList<FunnelData>();
    private List<FunnelData> actuData = new ArrayList<FunnelData>();
    private List<FunnelData> legendData = new ArrayList<FunnelData>();

    private XEnum.SortType mSortType = XEnum.SortType.DESC;
    private XEnum.FunnelType mFunType = XEnum.FunnelType.NORMAL;

    //图的初始宽度比
    private float mPlotWidthPercent = 100.f;
    private Paint mPaint = null;
    private Paint mPaintFunnelLine = null;
    private boolean mFunnelLineVisible = true;

    private Paint mPaintLabel = null;
    private Paint mPaintLabelLine = null;

    //同步标签颜色
    private boolean mIsLabelLineSyncColor = false;
    private boolean mIsLabelSyncColor = false;
    private boolean mIsShowLabelLine = false;
    private XEnum.HorizontalAlign mLabelAlign = XEnum.HorizontalAlign.CENTER;
    private boolean mLabelVisible = true;
    //是否显示图例
    private boolean isShowlegend = false;
    private int mCount = -1;
    private int mthisCount = -1;
    private long mStartTime = -1;
    private long mCountTime = 1000;
    private long mEndTime = -1;
    private long mThisTime = -1;
    private long mEveryTime = -1;

    public FunnelChart() {
    }


    private View mvThisView = null;

    public void setIsShowlegend(boolean legend) {
        this.isShowlegend = legend;
    }

    @Override
    public XEnum.ChartType getType() {
        return XEnum.ChartType.FUNNEL;
    }

    public View getMvThisView() {
        return mvThisView;
    }

    public void setMvThisView(View mvThisView) {
        this.mvThisView = mvThisView;
    }

    /**
     * 区域画笔
     *
     * @return 画笔
     */
    public Paint getPaint() {
        if (null == mPaint) mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        return mPaint;
    }

    public void setActuData(List<FunnelData> actuData) {
        this.actuData.clear();
        this.actuData.addAll(actuData);
    }

    public List<FunnelData> getmDataSet() {
        return mDataSet;
    }

    public List<FunnelData> getActuData() {
        return actuData;
    }

    /**
     * 各区域间的间隔线画笔
     *
     * @return 画笔
     */
    public Paint getFunnelLinePaint() {
        if (null == mPaintFunnelLine) mPaintFunnelLine = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaintFunnelLine.setStrokeWidth(5);
        return mPaintFunnelLine;
    }

    /**
     * 开放标签画笔
     *
     * @return 画笔
     */
    public Paint getLabelPaint() {
        if (null == mPaintLabel) mPaintLabel = new Paint(Paint.ANTI_ALIAS_FLAG);
        return mPaintLabel;
    }


    public XEnum.FunnelType getmFunType() {
        return mFunType;
    }

    /**
     * 设置漏斗图类型
     *
     * @param mFunType
     */
    public void setmFunType(XEnum.FunnelType mFunType) {
        this.mFunType = mFunType;
    }

    /**
     * 开放标签连接线画笔
     *
     * @return 画笔
     */
    public Paint getLabelLinePaint() {
        if (null == mPaintLabelLine) mPaintLabelLine = new Paint(Paint.ANTI_ALIAS_FLAG);
        return mPaintLabelLine;
    }

    /**
     * 设置用于绘图的宽度比例
     *
     * @param percent 比例
     */
    public void setPlotWidthPercent(float percent) {
        mPlotWidthPercent = percent;
    }

    /**
     * 设置报表的数据排序及显示方式
     *
     * @param type 显示方式
     */
    public void setSortType(XEnum.SortType type) {
        mSortType = type;
    }

    /**
     * 不显示标签连接线
     */
    public void hideLabelLine() {
        mIsShowLabelLine = false;
    }

    /**
     * 显示标签连接线
     */
    public void showLabelLine() {
        mIsShowLabelLine = true;
    }

    /**
     * 标签连接线显示状态
     *
     * @return 状态
     */
    public boolean isShowLabelLine() {
        return mIsShowLabelLine;
    }

    /**
     * 设置标签颜色与当地扇区颜色同步
     */
    public void syncLabelLineColor() {
        mIsLabelLineSyncColor = true;
    }

    /**
     * 设置折线标签颜色与当地扇区颜色同步
     */
    public void syncLabelColor() {
        mIsLabelSyncColor = true;
    }

    /**
     * 设置是否显示区域间隔线
     *
     * @param visible 是否显示
     */
    public void setFunnelLineVisible(boolean visible) {
        mFunnelLineVisible = visible;
    }

    /**
     * 返回是否显示区域间隔线
     *
     * @return 是否显示
     */
    public boolean getFunnelLineVisible() {
        return mFunnelLineVisible;
    }

    /**
     * 设置是否在线上显示标签
     *
     * @param visible 是否显示
     */
    public void setLabelVisible(boolean visible) {
        mLabelVisible = visible;
    }

    /**
     * 返回是否在线上显示标签
     *
     * @return 是否显示
     */
    public boolean getLabelVisible() {
        return mLabelVisible;
    }

    /**
     * 显示标签显示位置
     *
     * @param align 位置
     */
    public void setLabelAlign(XEnum.HorizontalAlign align) {
        mLabelAlign = align;

        switch (mLabelAlign) {
            case LEFT:
                getLabelPaint().setTextAlign(Align.LEFT);
                showLabelLine();
                break;
            case CENTER:
                getLabelPaint().setTextAlign(Align.CENTER);
                break;
            case RIGHT:
                getLabelPaint().setTextAlign(Align.RIGHT);
                showLabelLine();
                break;
            default:
                getLabelPaint().setTextAlign(Align.CENTER);
        }

    }

    /**
     * 返回标签显示位置
     *
     * @return 位置
     */
    public XEnum.HorizontalAlign getLabelAlign() {
        return mLabelAlign;
    }

    /**
     * 返回图的数据源
     *
     * @return 数据源
     */
    public List<FunnelData> getDataSource() {
        return mDataSet;
    }

    /**
     * 设置数据源
     *
     * @param dataSet 数据集
     */
    public void setDataSource(List<FunnelData> dataSet) {
        mDataSet.clear();
        mDataSet.addAll(dataSet);
        legendData.clear();
        legendData.addAll(dataSet);
    }

    private boolean sortDataSet() {
        if (null == mDataSet) {
            Log.e(TAG, "数据源为空!");
            return false;
        }

        for (int i = mDataSet.size() - 1; i >= 0; i--) {
            FunnelData d = mDataSet.get(i);
            if (Float.compare(d.getData(), 0.0f) == -1
                    || Float.compare(d.getData(), 0.0f) == 0) {
                mDataSet.remove(i);
            }
        }
        if (mFunType == contrast || mFunType == symmetry) {
            for (int i = actuData.size() - 1; i >= 0; i--) {
                FunnelData d = actuData.get(i);
                if (Float.compare(d.getData(), 0.0f) == -1
                        || Float.compare(d.getData(), 0.0f) == 0) {
                    actuData.remove(i);
                }
            }
        }
        if (mDataSet.size() == 0) return false;
//		if( XEnum.SortType.NORMAL != mSortType)Collections.sort(mDataSet);
//		if(actuData!=null&&actuData.size()>0&&XEnum.SortType.NORMAL != mSortType)Collections.sort(actuData);
        return true;
    }


    private void drawTriangle(Canvas canvas,
                              float cx, PointF start, PointF stop) {

        Path path = new Path();
        path.moveTo(start.x, start.y);
        path.lineTo(stop.x, stop.y);

        switch (mSortType) {
            case DESC:
                path.lineTo(cx, plotArea.getBottom());
                break;
            case ASC:
            case NORMAL:
            default:
                path.lineTo(cx, plotArea.getTop());
        }
        path.close();
        getPaint().setColor(mDataSet.get(0).getColor());
        canvas.drawPath(path, getPaint());
    }

    private float getHalfWidth(float funnelWidth, float data) {
        return funnelWidth * (data / 100) / 2;
    }

    private android.os.Handler mHandler = new android.os.Handler() {
        @Override
        public void handleMessage(Message msg) {

            super.handleMessage(msg);
        }
    };

    /**
     * 绘画 漏斗
     *
     * @param canvas
     * @param cx
     * @param funnelWidth  起始宽度
     * @param funnelHeight 每块高
     */
    protected void renderPlotDesc(Canvas canvas, float cx, float funnelWidth, float funnelHeight, List<FunnelData> data, int count) {
        float halfWidth = 0.f;
        float bottomY = plotArea.getBottom();

        PointF pStart = new PointF();
        PointF pStop = new PointF();

        pStart.x = cx - plotArea.getPlotWidth() / 2;
        pStop.x = cx + plotArea.getPlotWidth() / 2;
        pStart.y = pStop.y = plotArea.getBottom();

        halfWidth = funnelWidth / 2;

        float labelY = 0.f;

        Path path = new Path();
        for (int i = 0; i < count; i++) {
            FunnelData d = data.get(i);

            path.reset();
            if (i == 0) {
                path.moveTo(cx, plotArea.getBottom());
                saveBarRectFRecord(i, 0, cx - getHalfWidth(funnelWidth, d.getData()), pStart.y, cx + getHalfWidth(funnelWidth, d.getData()), plotArea.getBottom() - funnelHeight);
            } else {
                path.moveTo(pStart.x, pStart.y);
                path.lineTo(pStop.x, pStop.y);
            }
            halfWidth = getHalfWidth(funnelWidth, d.getData());

            bottomY = sub(plotArea.getBottom(), i * funnelHeight);

            labelY = bottomY - funnelHeight / 2;

            pStart.x = cx - halfWidth;
            pStart.y = bottomY - funnelHeight;

            pStop.x = cx + halfWidth;
            pStop.y = bottomY - funnelHeight;
            saveBarRectFRecord(i, 0, pStart.x, pStart.y + funnelHeight, pStop.x, pStop.y);
            path.lineTo(pStop.x, pStop.y);
            path.lineTo(pStart.x, pStart.y);

            this.getPaint().setColor(d.getColor());
            path.close();
            double countBL=Math.ceil((double)mCountTime/(double)d.getAlpha());
            double thisAlpha=Math.ceil((double)mThisTime/countBL);
            if(thisAlpha>d.getAlpha()){
                thisAlpha=d.getAlpha();
            }
            if (d.getAlpha() != -1) getPaint().setAlpha((int)thisAlpha);
            canvas.drawPath(path, this.getPaint());
            double countLine=Math.ceil((double)mCountTime/255);
            double thisAlphaLine=Math.ceil((double)mThisTime/countLine);
            if(thisAlphaLine>255){
                thisAlphaLine=255;
            }
            if (d.getAlpha() != -1) getPaint().setAlpha((int) thisAlphaLine);

            if (i != count - 1 && mFunnelLineVisible) {
                canvas.drawLine(pStart.x, pStart.y, pStop.x, pStop.y, this.getFunnelLinePaint());
            }
            renderLabels(canvas, d.getLabel(), cx, labelY, d.getColor());
            //保存位置
        }

    }

    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            SystemClock.sleep(2000);
            //通过Handler发送一个消息切换回主线程（mHandler所在的线程）
            mHandler.sendEmptyMessage(0);
        }
    });

    /**
     * 绘画 漏斗  从上向下绘制
     *
     * @param canvas
     * @param cx           绘画图标区域中心点x坐标
     * @param funnelWidth  起始宽度
     * @param funnelHeight 每块高
     */
    protected void renderOnePlotDesc(Canvas canvas, float cx, float funnelWidth, float funnelHeight, List<FunnelData> data, int count) {
        float halfWidth = 0.f;
        float bottomY = plotArea.getBottom();

        PointF pStart = new PointF();
        PointF pStop = new PointF();

        pStart.x = cx - plotArea.getPlotWidth() / 2;
        pStop.x = cx + plotArea.getPlotWidth() / 2;
        pStart.y = pStop.y = plotArea.getTop();

        halfWidth = funnelWidth / 2;

        float labelY = 0.f;

        Path path = new Path();
        for (int i = 0; i < count; i++) {
            FunnelData d = data.get(i);

            path.reset();
            if (i == 0) {
                path.moveTo(cx - funnelWidth / 2, plotArea.getPlotTop());
                path.lineTo(cx + funnelWidth / 2, plotArea.getPlotTop());
                saveBarRectFRecord(i, 0, cx - funnelWidth / 2, plotArea.getPlotTop(), cx + funnelWidth / 2, plotArea.getPlotTop() + funnelHeight);
            } else {
                path.moveTo(pStart.x, pStart.y);
                path.lineTo(pStop.x, pStop.y);
                saveBarRectFRecord(i, 0, pStart.x, pStart.y, pStop.x, pStop.y + funnelHeight);
            }
            halfWidth = getHalfWidth(funnelWidth, d.getData());

            bottomY = add(plotArea.getTop(), ((i + 1) * funnelHeight));

            labelY = bottomY - funnelHeight / 2;

            pStart.x = cx - halfWidth;
            pStart.y = bottomY;

            pStop.x = cx + halfWidth;
            pStop.y = bottomY;

            path.lineTo(pStop.x, pStop.y);
            path.lineTo(pStart.x, pStart.y);

            this.getPaint().setColor(d.getColor());
            path.close();
            double countBL=Math.ceil((double)mCountTime/(double)d.getAlpha());
            double thisAlpha=Math.ceil((double)mThisTime/countBL);
            if(thisAlpha>d.getAlpha()){
                thisAlpha=d.getAlpha();
            }
            if (d.getAlpha() != -1) getPaint().setAlpha((int) thisAlpha);
            canvas.drawPath(path, this.getPaint());
            double countLine=Math.ceil((double)mCountTime/255);
            double thisAlphaLine=Math.ceil((double)mThisTime/countLine);
            if(thisAlphaLine>255){
                thisAlphaLine=255;
            }
            if (d.getAlpha() != -1) getPaint().setAlpha((int) thisAlphaLine);

            if (i != count - 1 && mFunnelLineVisible) {
                canvas.drawLine(pStart.x, pStart.y, pStop.x, pStop.y, this.getFunnelLinePaint());
            }
            renderLabels(canvas, d.getLabel(), cx, labelY, d.getColor());
        }

    }


    /**
     * 从下向上绘制
     *
     * @param canvas
     * @param cx
     * @param funnelWidth
     * @param funnelHeight
     */
    protected void renderPlotAsc(Canvas canvas, float cx, float funnelWidth, float funnelHeight, List<FunnelData> data, int count) {
        float halfWidth = 0.f;
        float bottomY = plotArea.getBottom();

        PointF pStart = new PointF();
        PointF pStop = new PointF();

        pStart.x = cx - plotArea.getPlotWidth() / 2;
        pStop.x = cx + plotArea.getPlotWidth() / 2;
        pStart.y = pStop.y = plotArea.getBottom();

        float labelY = 0.f;
        halfWidth = funnelWidth / 2;

        Path path = new Path();
        for (int i = 0; i < count; i++) {
            FunnelData d = data.get(i);
            path.reset();
            if (i == 0)//三角
            {
                path.moveTo(cx, plotArea.getTop());
            } else {
                path.moveTo(pStart.x, pStart.y);
                path.lineTo(pStop.x, pStop.y);
            }

            halfWidth = getHalfWidth(funnelWidth, d.getData());
            bottomY = add(plotArea.getTop(), i * funnelHeight);

            labelY = bottomY + funnelHeight / 2;

            pStart.x = cx - halfWidth;
            pStart.y = bottomY + funnelHeight;

            pStop.x = cx + halfWidth;
            pStop.y = bottomY + funnelHeight;

            path.lineTo(pStop.x, pStop.y);
            path.lineTo(pStart.x, pStart.y);
            path.close();

            this.getPaint().setColor(d.getColor());
            double countBL=Math.ceil((double)mCountTime/(double)d.getAlpha());
            double thisAlpha=Math.ceil((double)mThisTime/countBL);
            if(thisAlpha>d.getAlpha()){
                thisAlpha=d.getAlpha();
            }
            if (d.getAlpha() != -1) getPaint().setAlpha((int) thisAlpha);
            canvas.drawPath(path, this.getPaint());
            double countLine=Math.ceil((double)mCountTime/255);
            double thisAlphaLine=Math.ceil((double)mThisTime/countLine);
            if(thisAlphaLine>255){
                thisAlphaLine=255;
            }
            if (d.getAlpha() != -1) getPaint().setAlpha((int) thisAlphaLine);

            if (i != count - 1 && mFunnelLineVisible) {
                canvas.drawLine(pStart.x, pStart.y, pStop.x, pStop.y, this.getFunnelLinePaint());
            }
            renderLabels(canvas, d.getLabel(), cx, labelY, d.getColor());
            //保存位置
            saveBarRectFRecord(i, 0, pStart.x, pStart.y, pStop.x, pStop.y - funnelHeight);
        }
    }


    /**
     * 绘画 对比漏斗  从低向上绘制
     *
     * @param canvas
     * @param cx           绘画图标区域中心点x坐标
     * @param funnelWidth  起始宽度
     * @param funnelHeight 每块高
     */
    protected void renderContrastPlotDesc(Canvas canvas, float cx, float funnelWidth, float funnelHeight, List<FunnelData> expectData, List<FunnelData> actualData, int count) {
        renderPlotDesc(canvas, cx, funnelWidth, funnelHeight, expectData, count);
        renderPlotDesc(canvas, cx, funnelWidth, funnelHeight, actualData, count);
    }


    /**
     * 绘画 对称漏斗  从上向下绘制
     *
     * @param canvas
     * @param cx           绘画图标区域中心点x坐标
     * @param funnelWidth  起始宽度
     * @param funnelHeight 每块高
     */
    protected void rendercontrastPlotDesc(Canvas canvas, float cx, float funnelWidth, float funnelHeight, List<FunnelData> data, List<FunnelData> actualData, int count) {
        float halfWidth = 0.f;
        float bottomY = plotArea.getBottom();

        PointF pStart = new PointF();
        PointF pStop = new PointF();
        pStart.x = cx - (plotArea.getPlotWidth() * data.get(0).getData() / mPlotWidthPercent / 4);
        pStop.x = cx + (plotArea.getPlotWidth() * actualData.get(0).getData() / mPlotWidthPercent / 4);
        pStart.y = pStop.y = plotArea.getTop();
        float labelY = 0.f;
        canvas.drawText(actualData.get(0).getKey().substring(0, 2), cx * 3 / 2 - pStart.x / 2, plotArea.getTop() - 20.f, getLabelPaint());
        canvas.drawText(data.get(0).getKey().substring(0, 2), cx / 2 + pStart.x / 2, plotArea.getTop() - 20.f, getLabelPaint());
        Path path = new Path();
        for (int i = 0; i < count; i++) {
            FunnelData d = data.get(i);
            path.reset();
            path.moveTo(pStart.x, pStart.y);
            path.lineTo(pStop.x, pStop.y);
            saveBarRectFRecord(i, 0, pStart.x, pStart.y, pStop.x, pStart.y + funnelHeight);
            bottomY = add(plotArea.getTop(), ((i + 1) * funnelHeight));
            labelY = bottomY - funnelHeight / 2;
            if (i != 0) {
                pStart.x = cx - (plotArea.getPlotWidth() * data.get(i).getData() / mPlotWidthPercent / 4);
                pStop.x = cx + (plotArea.getPlotWidth() * actualData.get(i).getData() / mPlotWidthPercent / 4);
            }
            pStart.y = bottomY;
            pStop.y = bottomY;
            path.lineTo(pStop.x, pStop.y);
            path.lineTo(pStart.x, pStart.y);
            this.getPaint().setColor(d.getColor());
            path.close();
            double countBL=Math.ceil((double)mCountTime/(double)d.getAlpha());
            double thisAlpha=Math.ceil((double)mThisTime/countBL);
            if(thisAlpha>d.getAlpha()){
                thisAlpha=d.getAlpha();
            }
            if (d.getAlpha() != -1) getPaint().setAlpha((int) thisAlpha);
            canvas.drawPath(path, this.getPaint());
            double countLine=Math.ceil((double)mCountTime/255);
            double thisAlphaLine=Math.ceil((double)mThisTime/countLine);
            if(thisAlphaLine>255){
                thisAlphaLine=255;
            }
            if (d.getAlpha() != -1) getPaint().setAlpha((int) thisAlphaLine);

            if (i != count - 1 && mFunnelLineVisible) {
                canvas.drawLine(pStart.x, pStart.y, pStop.x, pStop.y, this.getFunnelLinePaint());
            }
            renderLabels(canvas, d.getLabel(), cx, labelY, d.getColor());
        }
        canvas.drawLine(cx, plotArea.getTop(), cx, plotArea.getTop() + funnelHeight * count, this.getFunnelLinePaint());
    }

    /**
     * 只有一条数据源调用
     *
     * @param canvas
     * @param cx           图表区中心x坐标
     * @param funnelWidth
     * @param funnelHeight
     */
    private void renderPlotOne(Canvas canvas, float cx, float funnelWidth, float funnelHeight) {
        FunnelData d = mDataSet.get(0);
        float halfWidth = getHalfWidth(funnelWidth, d.getData());

        PointF pStart = new PointF();
        PointF pStop = new PointF();

        pStart.x = cx - halfWidth;
        pStop.x = cx + halfWidth;

        if (XEnum.SortType.DESC == mSortType) {
            pStart.y = pStop.y = plotArea.getTop();
        } else {
            pStart.y = pStop.y = plotArea.getBottom();
        }
        double countBL=Math.ceil((double)mCountTime/(double)d.getAlpha());
        double thisAlpha=Math.ceil((double)mThisTime/countBL);
        if(thisAlpha>d.getAlpha()){
            thisAlpha=d.getAlpha();
        }
        if (d.getAlpha() != -1) getPaint().setAlpha((int) thisAlpha);
        drawTriangle(canvas, cx, pStart, pStop);
        double countLine=Math.ceil((double)mCountTime/255);
        double thisAlphaLine=Math.ceil((double)mThisTime/countLine);
        if(thisAlphaLine>d.getAlpha()){
            thisAlphaLine=d.getAlpha();
        }
        if (d.getAlpha() != -1) getPaint().setAlpha((int) thisAlphaLine);

        float labelY = plotArea.getBottom() - plotArea.getHeight() / 2;
        renderLabels(canvas, d.getLabel(), cx, labelY, d.getColor());
        saveBarRectFRecord(mDataSet.size() + 1, 0, pStart.x, pStart.y, pStop.x, pStop.y - funnelHeight);
        return;
    }

    protected void renderLabels(Canvas canvas, String label, float cx, float y, int color) {
        if (!getLabelVisible()) return;
        if ("" == label) return;

        if (mIsLabelLineSyncColor) {
            getLabelLinePaint().setColor(color);
            getLabelPaint().setColor(color);
        } else if (mIsLabelSyncColor) {
            getLabelPaint().setColor(color);
        }

        if (isShowLabelLine()) {
            float labelWidth = DrawHelper.getInstance().getTextWidth(getLabelPaint(), label);
            switch (getLabelAlign()) {
                case LEFT:
                    canvas.drawLine(cx, y, plotArea.getLeft() + labelWidth, y, getLabelLinePaint());
                    break;
                case CENTER:
                    break;
                case RIGHT:
                    canvas.drawLine(cx, y, plotArea.getRight() - labelWidth, y, getLabelLinePaint());
                    break;
                default:
                    break;
            }
        }
        float labelX = 0.f, labelY = 0.f;
        switch (getLabelAlign()) {
            case LEFT:
                labelX = plotArea.getLeft();
                break;
            case CENTER:
                labelX = cx;
                break;
            case RIGHT:
                labelX = plotArea.getRight();
                break;
            default:
                labelX = cx;
        }
        labelY = y + (DrawHelper.getInstance().getPaintFontHeight(getLabelPaint()) / 3);
        canvas.drawText(label, labelX, labelY, getLabelPaint());
    }


    /**
     * @param canvas
     */
    protected void renderPlot(Canvas canvas) {
        if (!sortDataSet()) return;

        float funnelWidth = plotArea.getPlotWidth() * (mPlotWidthPercent / 100);
        float funnelHeight = this.plotArea.getHeight() / mCount;
        float cx = plotArea.getCenterX();
        if (1 == mCount) {
            renderPlotOne(canvas, cx, funnelWidth, funnelHeight);
        }
        switch (mFunType) {
            case NORMAL:
                renderPlotDesc(canvas, cx, funnelWidth, funnelHeight, mDataSet, mthisCount);
                break;
            case flat:
                renderOnePlotDesc(canvas, cx, funnelWidth, funnelHeight, mDataSet, mthisCount);
                break;
            case pyramid:
                renderPlotAsc(canvas, cx, funnelWidth, funnelHeight, mDataSet, mthisCount);
                break;
            case contrast:
                renderContrastPlotDesc(canvas, cx, funnelWidth, funnelHeight, mDataSet, actuData, mthisCount);
                break;
            case symmetry:
                rendercontrastPlotDesc(canvas, cx, funnelWidth, funnelHeight, mDataSet, actuData, mthisCount);
                break;
            default:
        }
    }

    @Override
    protected synchronized boolean postRender(Canvas canvas) throws Exception {
        try {
            if (mStartTime == -1) {
                mCount = mDataSet.size();
                mStartTime = SystemClock.uptimeMillis();
                mEndTime = mStartTime + mCountTime;
                mEveryTime = mCountTime / mCount;
            }
            mThisTime = SystemClock.uptimeMillis() - mStartTime;
            super.postRender(canvas);
            mthisCount = (int) Math.ceil((double) mThisTime / (double) mEveryTime);

            //计算主图表区范围
            calcPlotRange();
            //画Plot Area背景
            plotArea.render(canvas);
            //绘制标题
            renderTitle(canvas);
            //绘制图例
            if (isShowlegend) drawClipLegend(canvas, legendData);
            //绘制图表
            if(mthisCount>mCount){
                mthisCount=mCount;
            }
            renderPlot(canvas);
            //显示焦点
            renderFocusShape(canvas);
            //响应提示
            renderToolTip(canvas);
            if (mThisTime < mCountTime) {
                mvThisView.invalidate();
            }
        } catch (Exception e) {
            throw e;
        }
        return true;
    }

    /**
     * 返回当前点击点的信息
     *
     * @param x 点击点X坐标
     * @param y 点击点Y坐标
     * @return 返回对应的位置记录
     */
    public BarPosition getPositionRecord(float x, float y) {
        return getBarRecord(x, y);
    }

    /* 开放图例基类
     * @return	基类
     */
    public PlotLegendRender getPlotLegend() {
        //图例
        if (null == plotLegend) plotLegend = new PlotLegendRender(this);
        return plotLegend;
    }

    @Override
    protected void drawClipLegend(Canvas canvas, List<FunnelData> dataSet) {
        plotLegend.renderFunnelKey(canvas, dataSet);
    }

}
